package docker

import (
	"context"
	"fmt"
	"io"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
)

// DockerService provides methods to interact with Docker
type DockerService struct {
	client *client.Client
}

// ContainerInfo represents the information of a Docker container
type ContainerInfo struct {
	ID        string            `json:"id"`
	Name      string            `json:"name"`
	Image     string            `json:"image"`
	Status    string            `json:"status"`
	State     string            `json:"state"`
	Ports     []types.Port      `json:"ports"`
	Mounts    []types.MountPoint `json:"mounts"`
	PID       int               `json:"pid"`
	Command   string            `json:"command"`
	Created   int64             `json:"created"`
}

// NewDockerService creates a new Docker service
func NewDockerService() (*DockerService, error) {
	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
	if err != nil {
		return nil, fmt.Errorf("failed to create Docker client: %w", err)
	}

	return &DockerService{
		client: cli,
	}, nil
}

// Close closes the Docker client
func (d *DockerService) Close() error {
	if d.client != nil {
		return d.client.Close()
	}
	return nil
}

// ListContainers returns information about all containers
func (d *DockerService) ListContainers() ([]ContainerInfo, error) {
	containers, err := d.client.ContainerList(context.Background(), container.ListOptions{All: true})
	if err != nil {
		return nil, fmt.Errorf("failed to list containers: %w", err)
	}

	var containerInfos []ContainerInfo
	for _, c := range containers {
		containerInfo, err := d.GetContainerInfo(c.ID)
		if err != nil {
			// Log error but continue with other containers
			fmt.Printf("Failed to get info for container %s: %v\n", c.ID, err)
			continue
		}
		containerInfos = append(containerInfos, containerInfo)
	}

	return containerInfos, nil
}

// GetContainerInfo returns detailed information about a specific container
func (d *DockerService) GetContainerInfo(containerID string) (ContainerInfo, error) {
	var containerInfo ContainerInfo

	// Get container details
	containerJSON, err := d.client.ContainerInspect(context.Background(), containerID)
	if err != nil {
		return containerInfo, fmt.Errorf("failed to inspect container: %w", err)
	}

	// Get container stats
	// stats, err := d.client.ContainerStats(context.Background(), containerID, false)
	// if err != nil {
	// 	return containerInfo, fmt.Errorf("failed to get container stats: %w", err)
	// }
	// defer stats.Body.Close()

	// Extract container information
	containerInfo.ID = containerJSON.ID
	containerInfo.Image = containerJSON.Config.Image
	containerInfo.Status = containerJSON.State.Status
	containerInfo.State = containerJSON.State.Status
	containerInfo.Command = fmt.Sprintf("%v", containerJSON.Config.Cmd)
	
	// Extract container name (remove leading slash)
	if len(containerJSON.Name) > 0 && containerJSON.Name[0] == '/' {
		containerInfo.Name = containerJSON.Name[1:]
	} else {
		containerInfo.Name = containerJSON.Name
	}
	
	// Handle created time - parse the string time to Unix timestamp
	if containerJSON.Created != "" {
		if t, err := time.Parse(time.RFC3339Nano, containerJSON.Created); err == nil {
			containerInfo.Created = t.Unix()
		}
	}
	
	containerInfo.PID = containerJSON.State.Pid
	
	// Process ports
	// Extract ports from NetworkSettings
	if containerJSON.NetworkSettings != nil && containerJSON.NetworkSettings.Ports != nil {
		for containerPort, hostPorts := range containerJSON.NetworkSettings.Ports {
			// Parse the container port
			port := types.Port{
				PrivatePort: uint16(containerPort.Int()),
				Type:        containerPort.Proto(),
			}
			
			// If there are host ports, use the first one
			if len(hostPorts) > 0 {
				hostPort, err := nat.ParsePort(hostPorts[0].HostPort)
				if err == nil {
					port.PublicPort = uint16(hostPort)
				}
				port.IP = hostPorts[0].HostIP
			}
			
			containerInfo.Ports = append(containerInfo.Ports, port)
		}
	}
	
	// Process mounts
	containerInfo.Mounts = containerJSON.Mounts

	return containerInfo, nil
}

// StopContainer stops a container
func (d *DockerService) StopContainer(containerID string) error {
	timeout := 10 // seconds
	err := d.client.ContainerStop(context.Background(), containerID, container.StopOptions{Timeout: &timeout})
	if err != nil {
		return fmt.Errorf("failed to stop container: %w", err)
	}
	return nil
}

// RestartContainer restarts a container
func (d *DockerService) RestartContainer(containerID string) error {
	timeout := 10 // seconds
	err := d.client.ContainerRestart(context.Background(), containerID, container.StopOptions{Timeout: &timeout})
	if err != nil {
		return fmt.Errorf("failed to restart container: %w", err)
	}
	return nil
}

// RemoveContainer removes a container
func (d *DockerService) RemoveContainer(containerID string) error {
	err := d.client.ContainerRemove(context.Background(), containerID, container.RemoveOptions{})
	if err != nil {
		return fmt.Errorf("failed to remove container: %w", err)
	}
	return nil
}

// StartContainer starts a container
func (d *DockerService) StartContainer(containerID string) error {
	err := d.client.ContainerStart(context.Background(), containerID, container.StartOptions{})
	if err != nil {
		return fmt.Errorf("failed to start container: %w", err)
	}
	return nil
}

// GetContainerLogs returns the logs of a container
func (d *DockerService) GetContainerLogs(containerID string) (string, error) {
	options := container.LogsOptions{
		ShowStdout: true,
		ShowStderr: true,
		Tail:       "100",
	}

	reader, err := d.client.ContainerLogs(context.Background(), containerID, options)
	if err != nil {
		return "", fmt.Errorf("failed to get container logs: %w", err)
	}
	defer reader.Close()

	// Read logs
	logs, err := io.ReadAll(reader)
	if err != nil {
		return "", fmt.Errorf("failed to read container logs: %w", err)
	}

	return string(logs), nil
}